#include "Interface.h"

#include "misc/Event.h"
#include "misc/Debug.h"
#include <ncurses.h>
#include <iostream>
#include <ostream>
#include <fstream>

Poco::Mutex* Interface::mutex = new Poco::Mutex;	// Get a new mutex
/*********************************************************/
Interface& Interface::Instance( ) 
{
	mutex->lock();
	static Interface pInstance;
	mutex->unlock();
	return pInstance; 			// return the address of the sole instance
}
/*********************************************************/
void Interface::run()
{
	Debug& DEBUG = Debug::Instance();
	try {
		int					ch = '_';		// Character used for single character input

		mutex->lock();
		if ( Poco::Thread::current()->getName() != ifThreadName ) {
			DEBUG << "Interface :: Wrong Thread trying to enter Interface::Run(): " << Poco::Thread::current()->getName() << std::endl;
			mutex->unlock();
			return;
		}
		DEBUG << "Interface :: Started Internal Thread: " << uiThread.name() << std::endl;	

		input.clear();
		display();							// Update the console screen

		// Enter the main thread loop
		while ( !stopSignal ) {
			mutex->unlock();
			ch = '_';						// Avoid infinite loop when ch == RETURN key
			timeout(25);						// Specify a timeout for getch

			// KEY_ENTER or '\n' or 13?? 
			while ( true ) {
				ch = wgetch( stdscr );				// Get a single character without waiting for the return key
				if ( ch == ERR ) {
					if ( stopSignal ) return;
				} else {
					processKey( ch );			// Process the key stroke
				}

				if ( (char)ch == '\n' ) break;			// Break out of the loop if the key is the return key
			}

			mutex->lock();
		}
		mutex->unlock();
	} catch(...) {
		DEBUG << "Interface :: Unhandled exception in Interface::run()" << std::endl;
		throw;
	}
}
/*********************************************************/
void Interface::processKey(const int ch)
{
	std::string		arg, instr;				// Arguments and instruction split vars
	Event*			event = Event::Instance(inputEventStr);	// Get the event for user input

	mutex->lock();
	switch( ch ) {
	case '\n':	
		cmdQueue.push(input);					// Push it onto the command queue
		event->set();						// Set the user input event flag
		input.clear();						// Clear the input string as this command has been processed;
		break;
	case KEY_BACKSPACE:
		input = input.substr(0,input.size()-1);
		break;
	default:
		input.push_back(ch);
		break;
	}
	mutex->unlock();

	display();				// Update the screen
}
/*********************************************************/
void Interface::display()
{
	int					row, col;
	std::string				out;
	std::string				line;
	std::vector<std::string>		lines;

	mutex->lock();
	clear();				// Clear the screen
	getmaxyx(stdscr,row,col);		// get the number of rows and columns

	// Clear the vars & assign starting values
	line.clear();
	lines.clear();
	out = output.str();

	// Seperate the output buffer into single lines
	for ( unsigned int i = 0; i < out.size(); i++ ) {
		line.push_back(out[i]);
		if ( out[i] == '\n' ) {
			lines.push_back(line);
			line.clear();
		}
	}

	// Check to see if the last line ended with a newline or not
	if ( !line.empty() ) { 
		lines.push_back(line);
		line.clear();
	}

	// Now we have the seperate lines we can print em to the interface console
	int currRow = row-2;
	for ( int i = lines.size()-1; i >= 0; i-- ) {
		move(currRow,0);
		printw(lines[i].c_str());
		
		if ( currRow == 0 ) break;			// Were done, otherwise we'll be printing on an invalid location
		currRow--;
	}

	// Move the cursor to the lower left corner of the screen and display the current input
	move(row-1,0);
	printw(input.c_str());

	refresh();
	mutex->unlock();
}
/*********************************************************/
void Interface::start(const std::string& eventStr)
{
	inputEventStr = eventStr;
	Event*	event = Event::Instance(inputEventStr);
	Debug& DEBUG = Debug::Instance();
	std::ostringstream 	ostr;

	stopSignal = false;					// Set the thread stop signal to false ( we don't want the thread to terminate instantly after creation )

	// Initialize the console
	initscr();						// Start curses
	raw();							// Set raw mode for the interface
	keypad(stdscr, TRUE);					// We want to receive keypad entries ( F1, F2, ... )
	noecho();						// Don't echo typed characters

	mutex->lock();
	if ( uiThread.isRunning() ) {
		mutex->unlock();
		return;
	} else {
		// Init vars
		stopSignal = false;				// We are just getting started so we don't quit right away ;-)
		while ( !cmdQueue.empty() ) cmdQueue.pop();	// We want to start with an empty command queue
		event->reset();					// Reset the UI input event

		// Initialize the thread name for our input thread
		DEBUG << "Interface :: Starting Input Thread" << std::endl;
		ostr << "Interface:" << uiThread.id();
		uiThread.setName(ostr.str());
		ifThreadName = ostr.str();

		// Start the thread & release the mutex
		uiThread.start((*this));
		mutex->unlock();
		usleep(50);					// Give the thread some time
	}
}
/*********************************************************/
void Interface::stop()
{
	// Get the event/debug class
	Event* event = Event::Instance(inputEventStr);
	Debug& DEBUG = Debug::Instance();

	if ( !uiThread.isRunning() ) return;

	// Stop the interface thread
	mutex->lock();
	stopSignal = true;
	DEBUG << "Interface :: Waiting for input thread to stop" << std::endl;
	mutex->unlock();
	while ( uiThread.isRunning() ) {
		uiThread.tryJoin(25);
	}
	mutex->lock();
	endwin();
	event->reset();
	while ( !cmdQueue.empty() ) cmdQueue.pop();
	mutex->unlock();
	endwin();						// End curses interface
	usleep(50);						// Wait a bit
}
/*********************************************************/
bool Interface::pop()
{
	mutex->lock();
	if ( cmdQueue.empty() ) {
		mutex->unlock();
		return( false );
	}
	cmdQueue.pop();
	mutex->unlock();
	return( true );
}
/*********************************************************/
bool Interface::pop(std::string& cmd)
{
	if (!peek(cmd)) return( false );
	pop();
	return( true );
}
/*********************************************************/
bool Interface::peek(std::string& command)
{
	mutex->lock();
	if ( cmdQueue.empty() ) {
		mutex->unlock();
		return( false );
	}
	command = cmdQueue.front();
	mutex->unlock();
	return( true );
}
/*********************************************************/
std::string Interface::dump()
{
	return( output.str() );
}
/*********************************************************/
void Interface::dump(const std::string& file) 
{
	std::ofstream		fFile;

	fFile.open(file.c_str(), std::ios::trunc | std::ios::out);
	if ( !fFile.is_open() ) throw(std::logic_error("Failed to open file for log dump"));

	fFile << output.str();

	fFile.close();
}
/*********************************************************/
Interface::Interface(const Interface& iface)
{
}
/*********************************************************/
Interface::Interface()
{
}
/*********************************************************/
Interface::~Interface()
{
	delete( mutex );
}
/*********************************************************/
void Interface::flush()
{
	display();
}
/*********************************************************/
std::ostream& Interface::operator<<(const std::string& ostr)
{
	mutex->lock();
	output << ostr;
	mutex->unlock();
	return( output );
}
